package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/kpango/glg"
)

var IndexList = []string{"DATETIME", "IP", "STAFF_ID", "QUERY_TYPE1", "QUERY_TYPE2", "QUERY_VALUE1", "QUERY_VALUE2", "POST_ID", "ORG_ID"}

func ReadFromEverylog(tableInfo, query string) ([]map[string]string, error) {
	info := strings.Split(tableInfo, "/")
	if len(info) != 2 {
		return nil, fmt.Errorf("queryService.ReadFromEverylog: Bad value of appId/tablename: %s", tableInfo)
	}
	appId, tablename := info[0], info[1]
	q := strings.Split(query, "|and|")
	conds, err := ParseCondition(q)
	if err != nil {
		glg.Errorf("queryService.ReadFromEverylog: Failed to parse condition:", err)
		return nil, err
	}
	switch {
	case query == "":
		return QueryAll(appId, tablename)
	case !strings.Contains(query, "DATETIME"):
		return QueryWithoutDatetime(appId, tablename, conds)
	default:
		return QueryWithDatetime(appId, tablename, conds)
	}

}

func QueryAll(appId, tablename string) ([]map[string]string, error) {
	request := IdxQueryRequest{
		Key:           "_crmlog_",
		Partition:     "",
		Dt:            strconv.Itoa(time.Now().Year()),
		Column_prefix: "{\"DATETIME\":\"\"}",
	}
	args := BuildIdxQueryArgs(request)
	resp, err := SendIdxQueryRequest(appId, tablename, args)
	if err != nil {
		return nil, err
	}
	result := make([]string, 0)
	for i, _ := range resp.Data {
		for _, val := range resp.Data[i].Result {
			result = append(result, val)
		}
	}
	return UnmarshalResult(result)
}

func QueryWithoutDatetime(appId, tablename string, conds ConditionSet) ([]map[string]string, error) {
	idx, err := conds.BuildQueryIndex("")
	if err != nil {
		return nil, err
	}
	partition := ""
	if len(conds["STAFF_ID"]) != 0 {
		partition = conds["STAFF_ID"][0].Val[0:1]
	}
	request := IdxQueryRequest{
		Key:          "_crmlog_",
		Partition:    partition,
		Dt:           strconv.Itoa(time.Now().Year()),
		Column_equal: string(idx),
	}
	args := BuildIdxQueryArgs(request)
	resp, err := SendIdxQueryRequest(appId, tablename, args)
	if err != nil {
		return nil, err
	}
	result := make([]string, 0)
	for i, _ := range resp.Data {
		for _, val := range resp.Data[i].Result {
			result = append(result, val)
		}
	}
	return UnmarshalResult(result)
}

func QueryWithDatetime(appId, tablename string, conds ConditionSet) ([]map[string]string, error) {
	var iqRequest IdxQueryRequest
	results := make(map[string]string)
	mintime, maxtime, err := conds.ParseDateTime()
	if err != nil {
		return nil, err
	}
	for _, dtCond := range conds["DATETIME"] {
		idx, err := conds.BuildQueryIndex(dtCond.Val)
		if err != nil {
			return nil, err
		}
		partition := ""
		if len(conds["STAFF_ID"]) != 0 {
			partition = conds["STAFF_ID"][0].Val[0:1]
		}
		iqRequest.Key = "_crmlog_"
		iqRequest.Column_prefix = string(idx)
		iqRequest.Dt = dtCond.Val[0:6]
		iqRequest.Partition = partition
		args := BuildIdxQueryArgs(iqRequest)
		resp, err := SendIdxQueryRequest(appId, tablename, args)
		if err != nil {
			return nil, err
		}
		for i, _ := range resp.Data {
			for key, val := range resp.Data[i].Result {
				results[key] = val
			}
		}
	}
	var staff, ip string
	if len(conds["STAFF_ID"]) > 0 {
		staff = conds["STAFF_ID"][0].Val
	}
	if len(conds["IP"]) > 0 {
		staff = conds["IP"][0].Val
	}
	filter := KeyFilter{
		mintime: mintime,
		maxtime: maxtime,
		staffId: staff,
		ip:      ip,
	}
	res := filterResultByKey(results, filter)
	return UnmarshalResult(res)
}

type IdxQueryRequest struct {
	Key           string `json:"key_prefix,omitempty"`
	Column_prefix string `json:"column_prefix,omitempty"`
	Column_equal  string `json:"column_equal,omitempty"`
	Key_only      string `json:"key_only,omitempty"`
	Partition     string `json:"partition,omitempty"`
	Dt            string `json:"dt,omitempty"`
}

func BuildIdxQueryArgs(req IdxQueryRequest) string {
	var res string
	if req.Column_equal != "" {
		res = res + "&column_equal=" + req.Column_equal
	}
	if req.Column_prefix != "" {
		res = res + "&column_prefix=" + req.Column_prefix
	}
	if req.Dt != "" {
		res = res + "&dt=" + req.Dt
	}
	if req.Key != "" {
		res = res + "&key_prefix=" + req.Key
	}
	if req.Partition != "" {
		res = res + "&partition=" + req.Partition
	}
	if req.Key_only != "" {
		res = res + "&key_only=" + req.Key_only
	}
	res = strings.TrimLeft(res, "&")
	return res
}

type Response struct {
	Cost int       `json:"cost_ms,omitempty"`
	Data []RowData `json:"data,omitempty"`
}

type RowData struct {
	Partition string            `json:"partition,omitempty"`
	Dt        string            `json:"dt,omitempty"`
	Result    map[string]string `json:"result,omitempty"`
	Keys      []string          `json:"keys,omitempty"`
}

func SendIdxQueryRequest(appId, tablename, args string) (Response, error) {
	resp := Response{}
	result, err := http.Post(
		BuildUrl(appId, tablename),
		"application/x-www-form-urlencoded",
		strings.NewReader(args),
	)
	if err != nil {
		glg.Errorf("queryService.SendIdxQueryRequest: Failed to connect everylog: %v", err)
		return resp, err
	}
	defer result.Body.Close()
	body, err := ioutil.ReadAll(result.Body)
	if err != nil {
		return resp, err
	}
	if result.StatusCode != 200 {
		glg.Errorf("queryService.SendIdxQueryRequest: Everylog response: %d, %s", result.StatusCode, string(body))
		return resp, err
	}
	if err = json.Unmarshal(body, &resp); err != nil {
		return resp, err
	}
	return resp, nil
}

func BuildUrl(appid, tablename string) string {
	return fmt.Sprintf("http://%s:%s/everylog/%s/%s", *EverlogIp, *EverlogPort, appid, tablename)
}

func UnmarshalResult(rs []string) ([]map[string]string, error) {
	// var result []map[string]string
	var record map[string]string
	result := make([]map[string]string, 0)
	for _, r := range rs {
		if err := json.Unmarshal([]byte(r), &record); err != nil {
			glg.Errorf("queryService.UnmarshalResult: Failed to parse json: %v", err)
			continue
		}
		result = append(result, record)
	}
	return result, nil
}

type KeyFilter struct {
	mintime time.Time
	maxtime time.Time
	staffId string
	ip      string
}

func filterResultByKey(results map[string]string, filter KeyFilter) []string {
	var result []string
	for key, r := range results {
		s := strings.TrimPrefix(key, "_crmlog_")
		k := strings.Split(s, "|")
		if len(k) != 3 {
			glg.Errorf("queryService.filterResultByKey: Parse key error: %s", key)
			continue
		}
		staff, datetime, ip := k[0], k[1], k[2]
		if len(datetime) != 14 && len(datetime) != 17 {
			glg.Errorf("queryService.filterResultByKey: Failed to parse datetime:%s", datetime)
			continue
		}
		if len(datetime) == 14 {
			datetime = datetime + "000"
		}
		dt, err := time.Parse("20060102150405000", datetime)
		if err != nil {
			glg.Errorf("queryService.filterResultByKey: Failed to parse datetime: %v", err)
			continue
		}
		if !dt.Before(filter.mintime) &&
			!dt.After(filter.maxtime) &&
			(filter.staffId == "" || filter.staffId == staff) &&
			(filter.ip == "" || filter.ip == ip) {
			result = append(result, r)
		}
	}
	return result
}
